﻿using System;
using DevelopersTestPad.AppFrame;
using DevelopersTestPad.Remoting;
using DevelopersTestPad.Worksheets.DotNet.Interaction;
using Ooaavee.Support;

namespace DevelopersTestPad.Worksheets.DotNet
{
    internal partial class DotNetWorksheetView : WorksheetView
    {
        private readonly ScriptManager _scriptManager = new ScriptManager();

        public DotNetWorksheetView()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Gets the SourceEditor.
        /// </summary>
        private SourceEditor SourceEditor { get { return _sourceEditor; } }

        /// <summary>
        /// Gets the SubViews.
        /// </summary>
        private SubViewContainer SubViews { get { return _subViews; } }

        /// <summary>
        /// Gets the ScriptManager.
        /// </summary>
        private ScriptManager ScriptManager { get { return _scriptManager; } }

        /// <summary>
        /// Initializes the worksheet view.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();

            SourceEditor.Worksheet = Worksheet;
        }

        /// <summary>
        /// Activates the worksheet view.
        /// </summary>
        public override void Activate()
        {
            base.Activate();

            SourceEditor.Focus();           
        }

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        protected override void OnDisposing()
        {
            base.OnDisposing();

            ScriptManager.Dispose();
        }
      
        /// <summary>
        /// Executes the worksheet on the worksheet view.
        /// </summary>
        protected override void Execute()
        {
            InitializeForExecution();

            // Get a script wrapper.
            bool wasNewCreated;
            ScriptWrapper script = ScriptManager.GetScript(Worksheet.Type, GetCodeLines(), out wasNewCreated);

            // Subscribe to event handlers, if the new script wrapper was created.
            if (wasNewCreated)
            {
                script.BuildStarted += BuildStarted;
                script.BuildSucceed += BuildSucceed;
                script.BuildFailed += BuildFailed;
                script.RunStarted += RunStarted;
                script.RunCompleted += RunCompleted;
                script.RunFailed += RunFailed;
                script.ReceivePlainText += ReceivePlainText;
                script.ReceiveInteractionMessage += ReceiveInteractionMessage;

                // The ExecutionEnd event will be raised after the script execution has ended. 
                // Release the script wrapper in the ExecutionEnd event handler!
                script.ExecutionEnd += ExecutionEnd;
            }

            // And finally execute the script. 
            script.ExecuteAsync();
        }
       
        /// <summary>
        /// Occurs when a build has started.
        /// </summary>
        private void BuildStarted(object sender, ScriptWrapper.BuildStartedEventArgs e)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new EventHandler<ScriptWrapper.BuildStartedEventArgs>(BuildStarted), sender, e);                
            }
            else
            {
                //SubViews.Output.Add(e.Message);                
                AppContext.Current.OnAppNotificationReceived(new AppNotificationEventArgs { Type = AppNotificationType.WorksheetExecutionStarted } );
            }
        }

        /// <summary>
        /// Occurs when a build succeed.
        /// </summary>
        private void BuildSucceed(object sender, ScriptWrapper.BuildSucceedEventArgs e)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new EventHandler<ScriptWrapper.BuildSucceedEventArgs>(BuildSucceed), sender, e);
            }
            else
            {
                //SubViews.Output.Add(e.Message);    
            }
        }

        /// <summary>
        /// Occurs when a build has failed. 
        /// </summary>
        private void BuildFailed(object sender, ScriptWrapper.BuildFailedEventArgs e)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new EventHandler<ScriptWrapper.BuildFailedEventArgs>(BuildFailed), sender, e);
            }
            else
            {
                SubViews.Output.Add(e.Message);                
                SubViews.CompilerErrors.Add(e.Exception);
                ActivateSubView(SubViews.CompilerErrors);
                AppContext.Current.OnAppNotificationReceived(new AppNotificationEventArgs { Type = AppNotificationType.WorksheetExecutionEnded });
            }
        }

        /// <summary>
        /// Occurs when a run has been started. 
        /// </summary>
        private void RunStarted(object sender, ScriptWrapper.RunStartedEventArgs e)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new EventHandler<ScriptWrapper.RunStartedEventArgs>(RunStarted), sender, e);
            }
            else
            {
                // TODO: Tämä ohjattava jonnekin lokinäytölle tai vastaavaan paikkaan.
                //SubViews.Output.Add(e.Message);
            }
        }

        /// <summary>
        /// Occurs when a run has been completed.
        /// </summary>
        private void RunCompleted(object sender, ScriptWrapper.RunCompletedEventArgs e)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new EventHandler<ScriptWrapper.RunCompletedEventArgs>(RunCompleted), sender, e);
            }
            else
            {
                // TODO: Tämä ohjattava jonnekin lokinäytölle tai vastaavaan paikkaan.
                //SubViews.Output.Add(e.Message);

                AppContext.Current.OnAppNotificationReceived(new AppNotificationEventArgs { Type = AppNotificationType.WorksheetExecutionEnded });
            }
        }

        /// <summary>
        /// Occurs when a run has been failed. 
        /// </summary>
        private void RunFailed(object sender, ScriptWrapper.RunFailedEventArgs e)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new EventHandler<ScriptWrapper.RunFailedEventArgs>(RunFailed), sender, e);
            }
            else
            {
                SubViews.Output.Add(e.Message);
                SubViews.RuntimeExceptions.Add(e.Exception);
                ActivateSubView(SubViews.Output);
                AppContext.Current.OnAppNotificationReceived(new AppNotificationEventArgs { Type = AppNotificationType.WorksheetExecutionEnded });
            }
        }

        /// <summary>
        /// Occurs when received a plain text.
        /// </summary>
        private void ReceivePlainText(object sender, ConsoleWriterEventArgs e)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new EventHandler<ConsoleWriterEventArgs>(ReceivePlainText), sender, e);
            }
            else
            {
                SubViews.Console.Add(e.PlainText);
            }
        }

        /// <summary>
        /// Occurs when received an interaction message.
        /// </summary>
        private void ReceiveInteractionMessage(object sender, InteractionMessageEventArgs e)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new EventHandler<InteractionMessageEventArgs>(ReceiveInteractionMessage), sender, e);
            }
            else
            {
                HandleInteractionMessage(e);
            }
        }

        /// <summary>
        /// Occurs when the script execution has ended.
        /// </summary>
        private void ExecutionEnd(object sender, ScriptWrapper.ExecutionEndEventArgs e)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new EventHandler<ScriptWrapper.ExecutionEndEventArgs>(ExecutionEnd), sender, e);
            }
            else
            {
                ScriptManager.Release((ScriptWrapper)sender);
            }
        }

        /// <summary>
        /// Initializes the worksheet view for a script execution.
        /// </summary>
        private void InitializeForExecution()
        {
            SubViews.CompilerErrors.Clear();
        }

        /// <summary>
        /// Gets code lines to run in a script.
        /// </summary>
        /// <returns>The code lines.</returns>
        private string GetCodeLines()
        {
            string lines = SourceEditor.SelectedText;
            if (string.IsNullOrEmpty(lines))
            {
                lines = SourceEditor.Text;
            }
            return lines;
        }

        /// <summary>
        /// Handles an incoming interaction message.
        /// </summary>
        private void HandleInteractionMessage(InteractionMessageEventArgs e)
        {
            switch (e.Type)
            {
                case InteractionMessageEventArgs.InteractionMessageType.Text:
                    SubViews.Output.Add(e.Data.ToString());
                    ActivateSubView(SubViews.Output);
                    break;
                 case InteractionMessageEventArgs.InteractionMessageType.Push:
                    SubViews.PushOutput.Add((Reflector.ReflectedObject)e.Data);
                    ActivateSubView(SubViews.PushOutput);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        /// <summary>
        /// Activates the specified sub view and changes focus back to the source editor control.
        /// </summary>
        /// <param name="subView">The sub view to activate.</param>
        private void ActivateSubView(SubView subView)
        {
            SubViews.Activate(subView);
            SourceEditor.Focus();
        }
    }
}
